﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using VisualWip.Core.Interfaces;
using VisualWip.Core.Utils;

namespace VisualWip.Core
{
    public class PersistenceTree
    {
        private readonly List<PersistenceTree> _children;
        private readonly Func<object> _creater;
        private readonly Dictionary<string, object> _dict;
        private readonly string _name;
        private readonly Type _type;

        public PersistenceTree(string name, string typeName, Func<object> creater)

        {
            _creater = creater;
            _name = name;
            _type = Type.GetType(typeName);
            _dict = new Dictionary<string, object>();
            _children = new List<PersistenceTree>();
        }


        public PersistenceTree(string name, string typeName)
            : this(name, Type.GetType(typeName))
        {
        }

        public PersistenceTree(string name, object type)
        {
            _name = name;
            _type = type.GetType();
            _dict = new Dictionary<string, object>();
            _children = new List<PersistenceTree>();
            _creater = () => Activator.CreateInstance(_type);
        }

        public Type Type
        {
            get { return _type; }
        }

        public string Name
        {
            get { return _name; }
        }

        public Dictionary<string, object> Properties
        {
            get { return _dict; }
        }

        public List<PersistenceTree> Children
        {
            get { return _children; }
        }

        public void Add<T>(Expression<Func<T>> property)
        {
            Properties.Add(PropertyHelper.Name(property), property.Compile().Invoke());
        }

        public void Add(string propertyName, object value)
        {
            Properties.Add(propertyName, value);
        }

        public T Get<T>(Expression<Func<T>> property,T defaultValue)
        {
            return Get( PropertyHelper.Name(property),defaultValue);
        }


        public T Get<T>(string propertyName, T defaultValue)
        {
            if (Properties.ContainsKey(propertyName))
                return (T)Properties[propertyName];
            
            return defaultValue;
        }

        public void AddChildren<T>(Expression<Func<T>> property) where T : IEnumerable<IPersistable>
        {
            foreach (IPersistable setting in property.Compile().Invoke())
            {
                Children.Add(setting.Persist(PropertyHelper.Name(property)));
            }
        }

        public void AddChildren(IEnumerable<PersistenceTree> childen)
        {
            foreach (PersistenceTree child in childen)
            {
                Children.Add(child);
            }
        }

        public void AddChild<T>(Expression<Func<T>> property) where T : IPersistable
        {
            Children.Add(property.Compile().Invoke().Persist(PropertyHelper.Name(property)));
        }

        public void AddChild(PersistenceTree child)
        {
            Children.Add(child);
        }

        public IEnumerable<PersistenceTree> GetChildren<T>(Expression<Func<T>> property)
        {
            return GetFromChildren(PropertyHelper.Name(property));
        }

        public T GetChild<T>(Expression<Func<T>> property) where T : IPersistable
        {
            return GetChildren(property).FirstOrDefault().Create<T>();
        }

        public IEnumerable<PersistenceTree> GetChildren(string propertyName)
        {
            return GetFromChildren(propertyName);
        }

        private IEnumerable<PersistenceTree> GetFromChildren(string name)
        {
            return from settingsDictionary in Children
                   where settingsDictionary.Name == name
                   select settingsDictionary;
        }

        public T Create<T>() where T : IPersistable
        {
            var instance = (T) _creater.Invoke();
            instance.Load(this);
            return instance;
        }
    }
}